-- ENCODER INTEGRATION -- AIT Imaging Lab -- This code reads values from an encoder directly from an FPGA. ENC_A and ENC_B are the raw encoder inputs. -- These values are compared within ENC_PROCESS, where movement and direction values are obtained. -- A_ERROR, B_ERROR, CLOCKWISE, COUNTERCLOCKWISE, MOVEMENT, and COUNTER_0-17 are used to debug and show states on the FPGA using LEDs -- SEND_MSB and SEND_LSB are used to send a 2 bit signed value to an outside source which would calculate position outside the FPGA -- enc_pos_int is the encoder position as an integer, and enc_pos_slv is the encoder position as a STD_LOGIC_VECTOR -- USEFUL LINKS -- http://jugglingpirate.net/using-rotary-encoder-with-fpga-vhdl-code/ -- https://www.seas.upenn.edu/~ese171/vhdl/vhdl_primer.html#_Toc526061362 -- http://howtomechatronics.com/tutorials/arduino/rotary-encoder-works-use-arduino/ -- https://forum.allaboutcircuits.com/threads/vhdl-rotary-encoder-interface.23344/ -- A_STATE | A_PREVIOUS | B_STATE | B_PREVIOUS | DIRECTION ("C": CLOCKWISE, "CC": COUNTERCLOCKWISE, "-": NOT POSSIBLE) -- 0 | 0 | 0 | 0 | - --* 0 | 0 | 0 | 1 | C --* 0 | 0 | 1 | 0 | CC -- 0 | 0 | 1 | 1 | - --* 0 | 1 | 0 | 0 | CC -- 0 | 1 | 0 | 1 | - -- 0 | 1 | 1 | 0 | - --* 0 | 1 | 1 | 1 | C --* 1 | 0 | 0 | 0 | C -- 1 | 0 | 0 | 1 | - -- 1 | 0 | 1 | 0 | - --* 1 | 0 | 1 | 1 | CC -- 1 | 1 | 0 | 0 | - --* 1 | 1 | 0 | 1 | CC --* 1 | 1 | 1 | 0 | C -- 1 | 1 | 1 | 1 | - LIBRARY ieee; USE ieee.std_logic_1164.ALL; USE ieee.std_logic_unsigned.all; USE ieee.numeric_std.ALL; USE IEEE.std_logic_arith.all; LIBRARY altera_mf; USE altera_mf.all; ENTITY ENCODER_INTEGRATION_TOP IS PORT( CLK: IN STD_LOGIC; -- DEFINE CLOCK, INPUTS, AND OUTPUTS ENC_A: IN STD_LOGIC; ENC_B: IN STD_LOGIC; A_ERROR: OUT STD_LOGIC; B_ERROR: OUT STD_LOGIC; CLOCKWISE: OUT STD_LOGIC; COUNTERCLOCKWISE: OUT STD_LOGIC; MOVEMENT: OUT STD_LOGIC; SEND_MSB: OUT STD_LOGIC; SEND_LSB: OUT STD_LOGIC; COUNTER_0: OUT STD_LOGIC; COUNTER_1: OUT STD_LOGIC; COUNTER_2: OUT STD_LOGIC; COUNTER_3: OUT STD_LOGIC; COUNTER_4: OUT STD_LOGIC; COUNTER_5: OUT STD_LOGIC; COUNTER_6: OUT STD_LOGIC; COUNTER_7: OUT STD_LOGIC; COUNTER_8: OUT STD_LOGIC; COUNTER_9: OUT STD_LOGIC; COUNTER_10: OUT STD_LOGIC; COUNTER_11: OUT STD_LOGIC; COUNTER_12: OUT STD_LOGIC; COUNTER_13: OUT STD_LOGIC; COUNTER_14: OUT STD_LOGIC; COUNTER_15: OUT STD_LOGIC; COUNTER_16: OUT STD_LOGIC; COUNTER_17: OUT STD_LOGIC; TRIGGER: OUT STD_LOGIC ); END; ARCHITECTURE ARCH_ENCODER_INTEGRATION OF ENCODER_INTEGRATION_TOP IS -- ENCODER POSITION AS AN INTEGER SIGNAL enc_pos_int: INTEGER RANGE -(2**16+1) TO (2**16-1) := 0; -- ENCODER POSITION AS A STD_LOGIC_VECTOR SIGNAL enc_pos_slv: STD_LOGIC_VECTOR(17 downto 0) := "000000000000000000"; -- CURRENT STATE OF CHANNEL A AND B OF ENCODER SIGNAL state: STD_LOGIC_VECTOR(1 downto 0) := "00"; -- PREVIOIUS STATE OF CHANNEL A AND B OF ENCODER SIGNAL previous_state: STD_LOGIC_VECTOR(1 downto 0) := "00"; -- SHOWS WHETHER THERE IS MOVEMENT (DEBUGGING) SIGNAL movement_sig: STD_LOGIC := '0'; -- MSB OF 2-BIT SIGNED OUTPUT SIGNAL msb: STD_LOGIC; -- LSB OF 2-BIT SIGNED OUTPUT SIGNAL lsb: STD_LOGIC; -- COUNTS NUMBER OF TICS IN BETWEEN EACH TRIGGER, RESETS ONCE TRIGGER IS ACTIVATED SIGNAL trig_counter: INTEGER RANGE 0 TO 63 := 0; -- NUMBER OF TICS IN BETWEEN EACH TRIGGER CONSTANT trig_const: INTEGER := 0; -- CREATE ARRAY TYPE TYPE ARRAY_1D IS ARRAY (0 to 11) OF INTEGER; -- ARRAY OF POSITION VALUES TO TRIGGER ON SIGNAL pos_array: ARRAY_1D := (0, 500, 1000, 1500, 2000, 2500, 3000, 4500, 5000, 5500, 6000, 6500); -- INDEX OF POS_ARRAY SIGNAL pos_index: INTEGER := 1; BEGIN -- OUTPUT RAW VALUES FROM THE ENCODER A_ERROR <= ENC_A; B_ERROR <= ENC_B; -- SEND THE 2 BIT SIGNED NUMBER (00 = NO MOVEMENT, 01 = CLOCKWISE, 11 = COUNTERCLOCKWISE) SEND_MSB <= msb; SEND_LSB <= lsb; -- CONVERT enc_pos_int TO STD_LOGIC_VECTOR TYPE enc_pos_slv <= STD_LOGIC_VECTOR(TO_UNSIGNED(enc_pos_int,enc_pos_slv'LENGTH)); -- ASSIGN EACH BIT TO AN LED COUNTER_0 <= enc_pos_slv(0); COUNTER_1 <= enc_pos_slv(1); COUNTER_2 <= enc_pos_slv(2); COUNTER_3 <= enc_pos_slv(3); COUNTER_4 <= enc_pos_slv(4); COUNTER_5 <= enc_pos_slv(5); COUNTER_6 <= enc_pos_slv(6); COUNTER_7 <= enc_pos_slv(7); COUNTER_8 <= enc_pos_slv(8); COUNTER_9 <= enc_pos_slv(9); COUNTER_10 <= enc_pos_slv(10); COUNTER_11 <= enc_pos_slv(11); COUNTER_12 <= enc_pos_slv(12); COUNTER_13 <= enc_pos_slv(13); COUNTER_14 <= enc_pos_slv(14); COUNTER_15 <= enc_pos_slv(15); COUNTER_16 <= enc_pos_slv(16); COUNTER_17 <= enc_pos_slv(17); -- LOGIC THAT CONVERTS INPUTS TO 2 BIT STD_LOGIC "msb:lsb" -- "00" = NO MOVEMENT, "01" = CLOCKWISE, "11" = COUNTERCLOCKWISE -- A1^B2^B1^A2&(A1^B2): A1^B2^B1^A2 -- SEND_MSB <= ((NOT A_STATE) AND (NOT A_PREVIOUS) AND (B_STATE) AND (NOT B_PREVIOUS)) OR -- ((NOT A_STATE) AND (A_PREVIOUS) AND (NOT B_STATE) AND (NOT B_PREVIOUS)) OR -- ((A_STATE) AND (NOT A_PREVIOUS) AND (B_STATE) AND (B_PREVIOUS)) OR -- ((A_STATE) AND (A_PREVIOUS) AND (NOT B_STATE) AND (B_PREVIOUS)); -- -- SEND_MSB <= (previous_state(0) XOR state(1) XOR previous_state(1) XOR state(0)) AND (previous_state(0) XOR state(1)); -- SEND_LSB <= previous_state(0) XOR state(1) XOR previous_state(1) XOR state(0); -- ENC_PROCESS: PROCESS (CLK) IS BEGIN IF RISING_EDGE(CLK) THEN previous_state <= state; -- PUT INPUT VALUES INTO SIGNALS state(0) <= ENC_A; state(1) <= ENC_B; MOVEMENT <= movement_sig; IF (state(0) = '1' AND previous_state(0) = '0') THEN -- ENC_A RISING EDGE IF (ENC_B='0') THEN -- CLOCKWISE CLOCKWISE <= '1'; movement_sig <= '1'; COUNTERCLOCKWISE <= '0'; msb <= '1'; lsb <= '1'; enc_pos_int <= enc_pos_int + 1; trig_counter <= trig_counter + 1; ELSIF (ENC_B='1') THEN -- COUNTERCLOCKWISE CLOCKWISE <= '0'; movement_sig <= '1'; COUNTERCLOCKWISE <= '1'; msb <= '0'; lsb <= '1'; enc_pos_int <= enc_pos_int - 1; trig_counter <= trig_counter - 1; END IF; ELSIF (state(1) = '1' AND previous_state(1) = '0') THEN -- ENC_B RISING EDGE IF (ENC_A='1') THEN -- CLOCKWISE CLOCKWISE <= '1'; movement_sig <= '1'; COUNTERCLOCKWISE <= '0'; msb <= '1'; lsb <= '1'; enc_pos_int <= enc_pos_int + 1; trig_counter <= trig_counter + 1; ELSIF (ENC_A='0') THEN -- COUNTERCLOCKWISE CLOCKWISE <= '0'; movement_sig <= '1'; COUNTERCLOCKWISE <= '1'; msb <= '0'; lsb <= '1'; enc_pos_int <= enc_pos_int - 1; trig_counter <= trig_counter - 1; END IF; ELSIF (state(0) = '0' AND previous_state(0) = '1') THEN -- ENC_A FALLING EDGE IF (ENC_B='1') THEN -- CLOCKWISE CLOCKWISE <= '1'; movement_sig <= '1'; COUNTERCLOCKWISE <= '0'; msb <= '1'; lsb <= '1'; enc_pos_int <= enc_pos_int + 1; trig_counter <= trig_counter + 1; ELSIF (ENC_B='0') THEN -- COUNTERCLOCKWISE CLOCKWISE <= '0'; movement_sig <= '1'; COUNTERCLOCKWISE <= '1'; msb <= '0'; lsb <= '1'; enc_pos_int <= enc_pos_int - 1; trig_counter <= trig_counter - 1; END IF; ELSIF (state(1) = '0' AND previous_state(1) = '1') THEN -- ENC_B FALLING EDGE IF (ENC_A='0') THEN -- CLOCKWISE CLOCKWISE <= '1'; movement_sig <= '1'; COUNTERCLOCKWISE <= '0'; msb <= '1'; lsb <= '1'; enc_pos_int <= enc_pos_int + 1; trig_counter <= trig_counter + 1; ELSIF (ENC_A='1') THEN -- COUNTERCLOCKWISE CLOCKWISE <= '0'; movement_sig <= '1'; COUNTERCLOCKWISE <= '1'; msb <= '0'; lsb <= '1'; enc_pos_int <= enc_pos_int - 1; trig_counter <= trig_counter - 1; END IF; ELSE enc_pos_int <= enc_pos_int; trig_counter <= trig_counter; -- CLOCKWISE <= '0'; -- movement_sig <= '0'; -- COUNTERCLOCKWISE <= '0'; -- msb <= '0'; -- lsb <= '0'; END IF; -- -- SEND A TRIGGER SIGNAL EVERY TIME THE COUNTER EQUALS THE COUNTER CONSTANT -- IF (trig_counter = trig_const) THEN -- -- trig_counter <= 0; -- TRIGGER <= '1'; -- ELSE -- TRIGGER <= '0'; -- END IF; -- CHECK WHETHER ENCODER POSITION IS EQUAL TO CURRENT TRIGGER POSITION IF (enc_pos_int = pos_array(pos_index)) THEN -- SEND TRIGGER SIGNAL TRIGGER <= '1'; pos_index <= pos_index; ELSIF (enc_pos_int = pos_array(pos_index + 1)) THEN -- SEND TRIGGER SIGNAL TRIGGER <= '1'; pos_index <= pos_index + 1; ELSIF (enc_pos_int = pos_array(pos_index - 1)) THEN -- SEND TRIGGER SIGNAL TRIGGER <= '1'; pos_index <= pos_index - 1; ELSE TRIGGER <= '0'; END IF; END IF; END PROCESS ENC_PROCESS; END ARCH_ENCODER_INTEGRATION;